-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[google_maps_flutter_web] Add Advanced markers support (web) #9773
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[google_maps_flutter_web] Add Advanced markers support (web) #9773
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This PR adds support for Advanced Markers to the web implementation of google_maps_flutter. The changes are extensive and well-structured, refactoring the marker handling logic to support both legacy and advanced markers through generics and abstract classes. New tests have been added for the new functionality, and existing ones have been updated. The implementation looks solid, but I've found a couple of critical issues that would prevent the code from compiling or running correctly.
| clusterManagersController: clusterManagersController! | ||
| as ClusterManagersController<gmaps.AdvancedMarkerElement>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a typo here. clusterManagersController is used, but it should be _clusterManagersController. This will cause a compilation error as clusterManagersController is not defined in this scope.
clusterManagersController: _clusterManagersController!
as ClusterManagersController<gmaps.AdvancedMarkerElement>,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Obviously this does compile, but it does make it order-sensitive to when the private variable is set, which we don't want, so this should use _clusterManagersController.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
Outdated
Show resolved
Hide resolved
packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart
Show resolved
Hide resolved
| clusterManagersController: clusterManagersController! | ||
| as ClusterManagersController<gmaps.AdvancedMarkerElement>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Obviously this does compile, but it does make it order-sensitive to when the private variable is set, which we don't want, so this should use _clusterManagersController.
packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart
Show resolved
Hide resolved
packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart
Outdated
Show resolved
Hide resolved
| sanitize_html: ^2.0.0 | ||
| stream_transform: ^2.0.0 | ||
| web: ">=0.5.1 <2.0.0" | ||
| web: ">=1.0.0 <2.0.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can just be ^1.0.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
From triage: @aednlaxer Are you still planning on updating this PR? |
|
FYI: After merging in |
Yes, please don't close it |
9da73a7 to
65b8c81
Compare
mdebbar
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, it looks good to me! I just have a few minor code improvements.
| options.glyphColor = _getCssColor(circleGlyph.color); | ||
| case final TextGlyph textGlyph: | ||
| final web.Element element = web.document.createElement('p'); | ||
| element.innerHTML = textGlyph.text.toJS; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's safer to use element.text:
| element.innerHTML = textGlyph.text.toJS; | |
| element.text = textGlyph.text; |
| element.setAttribute( | ||
| 'style', | ||
| 'color: ${_getCssColor(textGlyph.textColor!)}', | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| element.setAttribute( | |
| 'style', | |
| 'color: ${_getCssColor(textGlyph.textColor!)}', | |
| ); | |
| element.style.color = _getCssColor(textGlyph.textColor!); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Element type doesn't have style property:
https://github.com/dart-lang/web/blob/5a7d0be70a258252b95bac6b900f26d6dae4d433/web/lib/src/dom/dom.dart#L3132
| icon.setAttribute( | ||
| 'style', | ||
| <String>[ | ||
| if (size != null) ...<String>[ | ||
| 'width: ${size.width.toStringAsFixed(1)}px;', | ||
| 'height: ${size.height.toStringAsFixed(1)}px;', | ||
| ], | ||
| if (opacity != null) 'opacity: $opacity;', | ||
| if (isVisible != null) 'visibility: ${isVisible ? 'visible' : 'hidden'};', | ||
| ].join(' '), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| icon.setAttribute( | |
| 'style', | |
| <String>[ | |
| if (size != null) ...<String>[ | |
| 'width: ${size.width.toStringAsFixed(1)}px;', | |
| 'height: ${size.height.toStringAsFixed(1)}px;', | |
| ], | |
| if (opacity != null) 'opacity: $opacity;', | |
| if (isVisible != null) 'visibility: ${isVisible ? 'visible' : 'hidden'};', | |
| ].join(' '), | |
| final iconStyle = icon.style; | |
| if (size != null) { | |
| iconStyle | |
| ..width = '${size.width.toStringAsFixed(1)}px' | |
| ..height = '${size.height.toStringAsFixed(1)}px'; | |
| } | |
| if (opacity != null) { | |
| iconStyle.opacity = opacity; | |
| } | |
| if (isVisible != null) { | |
| iconStyle.visibility = isVisible ? 'visible' : 'hidden'; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Icon type of the web package doesn't have style property either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making icon of type HTMLImageElement should make it work.
| }); | ||
|
|
||
| @override | ||
| void initializeMarkerListener({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We typically use the "add listener" terminology in the flutter code base: https://api.flutter.dev/flutter/search.html?q=addlistener
What do you think about renaming this to addMarkerListeners?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to keep the naming consistent. I have changed the function name to addMarkerListener.
| if (_infoWindow != null && newInfoWindowContent != null) { | ||
| _infoWindow.content = newInfoWindowContent; | ||
| if (onTap != null) { | ||
| marker.onClick.listen((gmaps.MapMouseEvent event) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to unsubscribe from these listeners when the marker is removed?
If yes, it can easily be done:
_subscriptions = [
if (onTap != null) marker.onClick.listen((_) => onTap.call()),
if (onDragStart != null) marker.onDragstart.listen((event) {
marker.position = event.latLng;
onDragStart.call(event.latLng ?? _nullGmapsLatLng);
}),
...
];and later in void remove():
_subscriptions?.forEach((sub) => sub.cancel);
_subscriptions = null;There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
| required VoidCallback? onTap, | ||
| }) { | ||
| if (onTap != null) { | ||
| marker.onClick.listen((gmaps.MapMouseEvent event) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question about unsubscribing from listeners.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
c1047c3 to
f72d139
Compare
mdebbar
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some suggestions to make element.style and icon.style available.
| final web.Element icon = web.document.createElement('img') | ||
| ..setAttribute('src', url); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use a more specific type so we can use icon.style:
| final web.Element icon = web.document.createElement('img') | |
| ..setAttribute('src', url); | |
| final web.HTMLImageElement icon = web.HTMLImageElement()..src = url; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion. All changes done.
| final web.Element icon = web.document.createElement('img')..setAttribute( | ||
| 'src', | ||
| ui_web.assetManager.getAssetUrl(iconConfig[1]! as String), | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
| final web.Element icon = web.document.createElement('img') | ||
| ..setAttribute('src', web.URL.createObjectURL(blob as JSObject)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
| icon.setAttribute( | ||
| 'style', | ||
| <String>[ | ||
| if (size != null) ...<String>[ | ||
| 'width: ${size.width.toStringAsFixed(1)}px;', | ||
| 'height: ${size.height.toStringAsFixed(1)}px;', | ||
| ], | ||
| if (opacity != null) 'opacity: $opacity;', | ||
| if (isVisible != null) 'visibility: ${isVisible ? 'visible' : 'hidden'};', | ||
| ].join(' '), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making icon of type HTMLImageElement should make it work.
| final web.Element element = web.document.createElement('p'); | ||
| element.text = textGlyph.text; | ||
| if (textGlyph.textColor != null) { | ||
| element.setAttribute( | ||
| 'style', | ||
| 'color: ${_getCssColor(textGlyph.textColor!)}', | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a HTMLParagraphElement type will allow you to use element.style:
| final web.Element element = web.document.createElement('p'); | |
| element.text = textGlyph.text; | |
| if (textGlyph.textColor != null) { | |
| element.setAttribute( | |
| 'style', | |
| 'color: ${_getCssColor(textGlyph.textColor!)}', | |
| ); | |
| final web.HTMLParagraphElement element = web.HTMLParagraphElement(); | |
| element.text = textGlyph.text; | |
| if (textGlyph.textColor != null) { | |
| element.style.color = _getCssColor(textGlyph.textColor!); |
mdebbar
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Thank you!
4cbf9d7 to
71404f1
Compare
71404f1 to
f30050c
Compare
packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
Outdated
Show resolved
Hide resolved
packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
Outdated
Show resolved
Hide resolved
packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
Outdated
Show resolved
Hide resolved
packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
Outdated
Show resolved
Hide resolved
packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
Outdated
Show resolved
Hide resolved
packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart
Show resolved
Hide resolved
packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart
Outdated
Show resolved
Hide resolved
packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart
Show resolved
Hide resolved
stuartmorgan-g
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
| @override | ||
| Future<bool> isAdvancedMarkersAvailable({required int mapId}) async { | ||
| final GoogleMapController map = _map(mapId); | ||
| return map.isAdvancedMarkersAvailable(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: why not just return _map(mapId).isAdvancedMarkersAvailable() like the other methods? The intermediate variable doesn't seem to be adding anything here.
383a245 to
340adb9
Compare
|
@mdebbar @stuartmorgan-g Since the |
This PR adds Advanced markers support to the web implementation of
google_maps_flutter.Approved combined PR: #7882
Approved and merged platform interface PR: #9737
Issue: #155526
Pre-Review Checklist
[shared_preferences]pubspec.yamlwith an appropriate new version according to the [pub versioning philosophy], or I have commented below to indicate which [version change exemption] this PR falls under[^1].CHANGELOG.mdto add a description of the change, [following repository CHANGELOG style], or I have commented below to indicate which [CHANGELOG exemption] this PR falls under[^1].///).